package com.ihr360.rest.webmvc;

import com.ihr360.commons.exception.Ihr360SimpleResultException;
import com.ihr360.rest.config.Ihr360RestConfiguration;
import com.ihr360.rest.projector.EntityProjector;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.ResourceAssembler;
import org.springframework.hateoas.ResourceSupport;
import org.springframework.hateoas.Resources;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.util.ArrayList;
import java.util.List;

@Component
public final class ResourceUtils {
    @Autowired
    Ihr360RestConfiguration ihr360RestConfiguration;
    @Autowired
    ProjectionFactory projectionFactory;

    @Autowired
    private PagedResourcesAssembler<Object> pagedResourcesAssembler;


    public ResponseEntity<?> toResource(final Object source, String projection) {
        if (source instanceof Iterable) {
            return ResponseEntity.ok(toResources((Iterable<?>) source, projection));
        } else if (source == null) {
            throw Ihr360SimpleResultException.builder().message("Resource not found").build();
        } else if (ClassUtils.isPrimitiveOrWrapper(source.getClass())) {
            return ResponseEntity.ok(source);
        } else {
            EntityProjector entityProjector = new EntityProjector(ihr360RestConfiguration.getProjectionConfiguration(), projectionFactory, projection);
            Object entity = entityProjector.project(source);
            return ResponseEntity.ok(new Resource(entity));
        }
    }

    public Resources<?> toResources(Iterable<?> source, String projection) {

        if (source instanceof Page) {
            Page<Object> page = (Page<Object>) source;
            return entitiesToResources(page, projection);
        } else if (source instanceof Iterable) {
            return entitiesToResources((Iterable<Object>) source, projection);
        } else {
            return new Resources(ControllerUtils.EMPTY_RESOURCE_LIST);
        }
    }

    public Resources<?> entitiesToResources(Page<Object> page, String projection) {
        if (page.getContent().isEmpty()) {
            return null;
        }
        EntityProjector entityProjector = new EntityProjector(ihr360RestConfiguration.getProjectionConfiguration(), projectionFactory, projection);
        return pagedResourcesAssembler.toResource(page,
                (ResourceAssembler<Object, ResourceSupport>) entity -> {
                    Object target = entityProjector.project(entity);
                    return new Ihr360EntityResource(target);
                });
    }

    public Resources<?> entitiesToResources(Iterable<Object> entities, String projection) {

        if (!entities.iterator().hasNext()) {
            List<Object> content = new ArrayList<>();
            return new Resources<>(content);
        }
        EntityProjector entityProjector = new EntityProjector(ihr360RestConfiguration.getProjectionConfiguration(), projectionFactory, projection);
        List<Resource<Object>> resources = new ArrayList<>();

        for (Object obj : entities) {
            if (obj == null) {
                resources.add(null);
                continue;
            }
            Object entity = entityProjector.project(obj);
            resources.add(entity == null ? null : new Resource(entity));
        }

        return new Resources<>(resources);
    }
}